const http = require('http');
const WebSocket = require('ws');
const { URL } = require('url');

// 创建 HTTP 服务器
const server = http.createServer(async (req, res) => {
  console.log(`Received HTTP request: ${req.url}`);

  // 判断是否是 /ws/internal 开头的请求
  if (req.url.startsWith('/ws/internal')) {
    try {
      // 提取路径和查询参数
      const url = new URL(req.url, `http://${req.headers.host}`);
      const path = url.pathname; // 获取路径部分（如 /ws/internal/abc）
      const query = url.searchParams; // 获取查询参数

      console.log(`Forwarding HTTP request to internal service via WebSocket: ${path}`);

      // 检查是否有一个有效的 WebSocket 连接到内网服务
      if (!internalConnection || internalConnection.readyState !== WebSocket.OPEN) {
        throw new Error('Internal WebSocket service is not available.');
      }

      // 构造请求消息并发送到内网服务
      // 这里是将公网过来的请求进行打包，然后发给内网的为服务器
      // 说是打包，其实就是将请求中的内容变成能够让websocket支持的传输方式，然后把信息发送给内网服务器
      const requestId = Date.now(); // 使用时间戳作为唯一 ID
      const requestMessage = JSON.stringify({
        id: requestId,
        method: req.method,
        path: path,
        headers: req.headers,
        query: Object.fromEntries(query.entries()),
      });

      console.log(`Sending request over WebSocket: ${requestMessage}`);

      // 存储请求的回调函数（用于处理响应）
      const responsePromise = new Promise((resolve, reject) => {
        pendingRequests[requestId] = { resolve, reject };
      });

      // 发送请求消息到内网服务
      internalConnection.send(requestMessage);

      // 等待内网服务的响应
      // 这里用await是为了利用这个可以阻塞后面的代码的操作
      // 只有这个返回出了结果之后才会执行后面的代码
      const responseData = await responsePromise;

      // 将内网服务的响应返回给客户端
      // 这里是只写了响应头的
      // 后面才是传输相应体
      res.writeHead(responseData.status, responseData.headers);
      
      // 判断是否是二进制数据、
      // 这里是对这个内网中请求的数据进行判断，判断是否是资源文件还是普通的http请求
      if (responseData.isFile) {
        // 由于内网服务器是将文件转换成base64格式的方式，所以需要这样进行转换
        // 为什么是转成base64格式而不是直接传输对象，这是因为websocket是不支持传输对象的
        // 除非是将对象转换成JSON格式的形式，JSON格式的形式，websocket是支持传输的
        // 其实webaocket是支持传输二进制形式的文件的，但是传输了二进制文件之后就无法传输其他的信息了
        // 比如传输id这种辨认身份的信息
        // 使用base64格式肯定是要低效很多的，首先base64格式的文件就要比原文件要大33%左右
        // 而且在进行转换的方面上也是需要花费时间和占用性能的
        const binaryData = Buffer.from(responseData.body, 'base64'); // 解码 Base64 数据
        // 如果是二进制数据，直接传递 Buffer
        res.end(binaryData);
      } else {
        // 如果是普通文本或 JSON 数据，序列化为字符串
        const responseBody = typeof responseData.body === 'object'
          ? JSON.stringify(responseData.body)
          : responseData.body;
        console.log("ssssssssssssssssssssssssssssssssssss", responseBody);

        res.end(responseBody);
      }
    } catch (error) {
      console.error('Error forwarding HTTP request:', error.message);

      // 如果发生错误，返回 500 错误
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: error.message || 'Internal server error' }));
    }
  } else {
    // 其他路径返回 404
    res.writeHead(404);
    res.end('Not Found');
  }
});

// 创建 WebSocket 服务
// 使用的是 noServer 模式，websocket 不会创建自己的http服务，而是用其他的方式创建的http服务
// 如果这里永乐其他的 http 服务，但是有永乐websocket 的http服务，这就会导致和其他的http服务冲突
// 或者是其他的http服务会覆盖这个websocket的服务，或者是websocket服务会覆盖其他的创建的http服务
const wss = new WebSocket.Server({ noServer: true });

// 存储内网服务的 WebSocket 连接
let internalConnection = null;

// 存储未完成的 HTTP 请求（通过 WebSocket 转发）
const pendingRequests = {};

server.on('upgrade', (request, socket, head) => {
  console.log('Upgrade request received.');

  // 判断是否是内网服务连接
  if (request.url === '/ws/internal') {
    console.log('Internal WebSocket service connected.');
    wss.handleUpgrade(request, socket, head, (ws) => {
      internalConnection = ws;

      ws.on('message', (message, isBinary) => {
        console.log("Received from internal WebSocket service");

        try {
          let response;
          // if (isBinary) {
          //   // 如果是二进制数据，直接解析为 Buffer
          //   response = {
          //     id: 1, // 假设二进制数据没有 ID（可以根据实际情况调整）
          //     data: message, // 直接存储二进制数据
          //   };
          //   // response = isBinary;
          // } else {
          //   // 如果是文本数据，尝试解析为 JSON
          //   response = JSON.parse(message.toString());
          // }

          response = JSON.parse(message.toString());

          // 检查响应是否包含有效的 id
          if (!response.id) {
            console.warn(`Invalid response received: missing or null id`);
            return;
          }

          // 找到对应的请求回调函数并调用
          const pendingRequest = pendingRequests[response.id];
          if (pendingRequest) {
            delete pendingRequests[response.id]; // 清除已处理的请求
            pendingRequest.resolve(response.data); // 返回响应数据
          } else {
            console.warn(`No pending request found for response ID: ${response.id}`);
          }
        } catch (error) {
          console.error('Error processing internal WebSocket response:', error.message);
        }
      });

      ws.on('close', () => {
        console.log('Internal WebSocket service disconnected.');
        internalConnection = null;
      });
    });
  } else {
    // 处理外网客户端连接
    console.log('Client WebSocket connected.');
    wss.handleUpgrade(request, socket, head, (ws) => {
      ws.on('message', (message) => {
        console.log(`Received from client WebSocket: ${message}`);

        // 将消息转发给内网服务
        if (internalConnection && internalConnection.readyState === WebSocket.OPEN) {
          internalConnection.send(message);
        } else {
          ws.send(JSON.stringify({ id: null, error: 'Internal service is not available.' }));
        }
      });

      ws.on('close', () => {
        console.log('Client WebSocket disconnected.');
      });
    });
  }
});

// 启动服务器
server.listen(8080, () => {
  console.log('Koa WebSocket server is listening on port 8080.');
});